package com.example.stagefrighth264;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * 
 * @author zyfgeliang
 * 
 */
public class VideoActivity extends Activity implements SurfaceHolder.Callback {
	private static final String Path_sdcrad = Environment
			.getExternalStorageDirectory().getAbsolutePath();
	// private static final String SAMPLE = Path_sdcrad + "/aiqingyu.mp4";
	private static final String SAMPLE = Path_sdcrad + "/1920x1080.264";
	// private static final String SAMPLE = Path_sdcrad + "/0.264";
	public static final String TAG = "DEecoseActivity";
	private PlayerThread mPlayer = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		SurfaceView sv = new SurfaceView(this);
		sv.getHolder().addCallback(this);
		setContentView(sv);
	}

	protected void onDestroy() {
		mPlayer.realse();
		super.onDestroy();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		if (mPlayer == null) {
			mPlayer = new PlayerThread(holder.getSurface());
			mPlayer.start();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		if (mPlayer != null) {
			mPlayer.interrupt();
		}
	}

	@SuppressLint("InlinedApi")
	private class PlayerThread extends Thread {
		private MediaExtractor extractor;
		private MediaCodec decoder;
		private Surface surface;

		public PlayerThread(Surface surface) {
			this.surface = surface;
		}

		public void realse() {
			if (decoder != null) {
				decoder.stop();
				decoder.release();
				decoder = null;
				isHasNext = false;
			}

		}

		int w = 320;
		int h = 240;
		int fps = 25;
		boolean isHasNext = true;

		@Override
		public void run() {

			// playbackFile();

			// init mediaformat
			MediaFormat mediaFormat = MediaFormat.createVideoFormat(
					"video/avc", w, h);
			mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 512000);
			mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
			mediaFormat
					.setInteger(
							MediaFormat.KEY_COLOR_FORMAT,
							MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
			String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
			decoder = MediaCodec.createDecoderByType(mime);
			if (decoder == null) {
				Log.e("DecodeActivity", "createDecoderByType fail!");
				return;
			}
			decoder.configure(mediaFormat, surface, null, 0);
			decoder.start();

			// get in/output buffer memory area from decoder
			ByteBuffer[] inputBuffers = decoder.getInputBuffers();
			ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
			// read some H264 raw data
			byte[] data = new byte[266249];
			// byte[] data = new byte[666249];
			FileInputStream fis;
			int byteCount = 0;
			long timeoutUs = 3000;
			long timestamp = 0l;
			try {
				fis = new FileInputStream(SAMPLE);
				while (isHasNext) {
					/** input ***/
					int inputBufferIndex = decoder
							.dequeueInputBuffer(timeoutUs);

					if (inputBufferIndex >= 0) {
						byteCount = fis.read(data);
						Log.i(TAG, "dequeueInputBuffer size" + byteCount+":"+data[0]);
						if (byteCount < 0) {
							isHasNext = false;
							decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0,
									MediaCodec.BUFFER_FLAG_END_OF_STREAM);
							break;
						} else {
							// put raw data to inputBuffer of decoder
							inputBuffers[inputBufferIndex].rewind();
							inputBuffers[inputBufferIndex].put(data, 0,
									byteCount);
							// get a inputBuffer memory index
							Log.i(TAG, "inputBufferIndex" + inputBufferIndex);
							// call decoder that inputBuffer has data
							decoder.queueInputBuffer(inputBufferIndex, 0,
									byteCount, timestamp, 0);
							timestamp += 40 * 1000;
						}

					}

					/** ouput ***/
					// get a outputBuffer memory index
					BufferInfo bufferInfo = new BufferInfo();
					// if inputBuffer is null ,this fun will be blocking
					int outIndex = decoder.dequeueOutputBuffer(bufferInfo,
							timeoutUs);
					Log.d(TAG, "dequeueOutputBuffer index " + outIndex);
					switch (outIndex) {
					case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
						Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
						outputBuffers = decoder.getOutputBuffers();
						break;
					case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
						// if the data is raw steam , coder will parse date
						// format
						Log.d("DecodeActivity",
								"New format " + decoder.getOutputFormat());
						break;
					case MediaCodec.INFO_TRY_AGAIN_LATER:
						Log.d("DecodeActivity",
								"dequeueOutputBuffer timed out AGAIN_LATER!");
						break;
					default:
						Log.d(TAG, "bufferInfo.flag " + bufferInfo.flags + "=="
								+ bufferInfo.size + "=="
								+ bufferInfo.presentationTimeUs);
						ByteBuffer buffer = outputBuffers[outIndex];
						try {
							sleep(100);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						Log.d(TAG, "yuv size" + buffer.position());
						decoder.releaseOutputBuffer(outIndex, true);

					}
					if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
						Log.d("DecodeActivity",
								"OutputBuffer BUFFER_FLAG_END_OF_STREAM is over");
						break;
					}
				}
				Log.w(TAG, "decode over");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		private void playbackFile() {
			extractor = new MediaExtractor();
			extractor.setDataSource(SAMPLE);

			for (int i = 0; i < extractor.getTrackCount(); i++) {
				MediaFormat format = extractor.getTrackFormat(i);
				String mime = format.getString(MediaFormat.KEY_MIME);
				if (mime.startsWith("video/")) {
					extractor.selectTrack(i);
					decoder = MediaCodec.createDecoderByType(mime);
					decoder.configure(format, surface, null, 0);
					break;
				}
			}

			if (decoder == null) {
				Log.e("DecodeActivity", "Can't find video info!");
				return;
			}

			decoder.start();

			ByteBuffer[] inputBuffers = decoder.getInputBuffers();
			ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
			BufferInfo info = new BufferInfo();
			boolean isEOS = false;
			long startMs = System.currentTimeMillis();

			while (!Thread.interrupted()) {
				if (!isEOS) {
					int inIndex = decoder.dequeueInputBuffer(10000);
					if (inIndex >= 0) {
						ByteBuffer buffer = inputBuffers[inIndex];
						int sampleSize = extractor.readSampleData(buffer, 0);
						if (sampleSize < 0) {
							Log.d("DecodeActivity",
									"InputBuffer BUFFER_FLAG_END_OF_STREAM");
							decoder.queueInputBuffer(inIndex, 0, 0, 0,
									MediaCodec.BUFFER_FLAG_END_OF_STREAM);
							isEOS = true;
						} else {
							Log.d("DecodeActivity", "readSampleData "
									+ extractor.getSampleTime());
							decoder.queueInputBuffer(inIndex, 0, sampleSize,
									extractor.getSampleTime(), 0);
							extractor.advance();
						}
					}
				}

				int outIndex = decoder.dequeueOutputBuffer(info, 10000);
				switch (outIndex) {
				case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
					Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
					outputBuffers = decoder.getOutputBuffers();
					break;
				case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
					Log.d("DecodeActivity",
							"New format " + decoder.getOutputFormat());
					break;
				case MediaCodec.INFO_TRY_AGAIN_LATER:
					Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
					break;
				default:
					ByteBuffer buffer = outputBuffers[outIndex];
					Log.v("DecodeActivity",
							"We can't use this buffer but render it due to the API limit, "
									+ info.presentationTimeUs + "="
									+ info.offset + "=" + info.size);
					while (info.presentationTimeUs / 1000 > System
							.currentTimeMillis() - startMs) {
						try {
							sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
							break;
						}
					}
					decoder.releaseOutputBuffer(outIndex, true);
					break;
				}

				if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
					Log.d("DecodeActivity",
							"OutputBuffer BUFFER_FLAG_END_OF_STREAM");
					break;
				}
			}

			decoder.stop();
			decoder.release();
			extractor.release();
		}
	}
}